package service

import (
	"context"
	"github.com/aliyun/alibabacloud-kms-agent/internal/cache"
	"github.com/aliyun/alibabacloud-kms-agent/internal/model"
)

func (s *Server) FetchSecret(ctx context.Context, params *model.GetSecretParams) (string, error) {
	secretId := params.SecretID
	versionId := params.VersionID
	versionStage := params.VersionStage

	cacheKey := generateCacheKey(secretId, versionId, versionStage)
	cacheValue, err := s.cacheStore.Get(ctx, cacheKey)
	if err != nil {
		s.loggerWrapper.Warn("get secret[%s] from cache err:%v.", params.SecretID, err)
	}

	var cacheSecret interface{}
	var isExpired bool
	if cacheValue != nil {
		cacheSecret = cacheValue.(*cache.Item).GetValue()
		isExpired = cacheValue.(*cache.Item).CheckExpired()
	}

	if cacheSecret != nil && !isExpired {
		s.loggerWrapper.Info("get secret[%s] from cache success.", params.SecretID)
		return cacheSecret.(string), nil
	}

	kmsSecret, err := s.kmsClient.GetSecretInfo(ctx, secretId, versionId, versionStage)
	if err != nil {
		s.loggerWrapper.Error("get secret[%s] from kms error:%v", params.SecretID, err)
		// Ignore transient errors
		if s.IgnoreTransientErrors && cacheSecret != nil && isIgnoreTransientErrors(err) {
			s.loggerWrapper.Warn("get expired secret[%s] from cache.", params.SecretID)
			return cacheSecret.(string), nil
		}
		return "", err
	}
	s.loggerWrapper.Info("get secret[%s] from kms.", params.SecretID)

	err = s.cacheStore.Put(ctx, cacheKey, kmsSecret)
	if err != nil {
		s.loggerWrapper.Warn("put secret to cache err:%v", err)
	}
	return kmsSecret, nil
}

/*
return secret_secretId_versionId_versionStage
*/
func generateCacheKey(secretId string, versionId string, versionStage string) string {
	cacheKey := "secret_" + secretId
	if versionId != "" {
		cacheKey = "_" + versionId
	}
	if versionStage != "" {
		cacheKey = "_" + versionStage
	}
	return cacheKey
}

func isIgnoreTransientErrors(err error) bool {
	if httpErr, ok := err.(*model.ErrorResponse); ok {
		// client errors that cannot be ignored
		if httpErr.StatusCode >= 400 && httpErr.StatusCode < 500 && httpErr.StatusCode != 429 {
			return false
		}
	}
	return true
}
